******************************************************************************
* This information is intended for advanced programmers writing applications
* or drivers to make use of the extended capabilities of the Paradise* VGA
* Plus 16* Card. This information is not needed for normal use of the VGA Plus
* 16 Card. Use of the information contained here requires familiarity with
* assembly language programming and the workings of the IBM* PC/AT* 
* environments.
*******************************************************************************

                     Extended Memory Mapping



The memory map for IBM PCs and compatibles allots 128 Kbytes of the 
one Megabyte system total to video RAM. This area begins at A0000 
and extends to BFFFF, but "standard" video modes only use:

 

     ADDRESS   SIZE      USED IN MODES

      A0000     64K      D,E,F,10,11,12,13

      B0000     32K      7

      B0000     64K      Hercules

      B8000     32K      0,1,2,3,4,5,6



If two video cards coexist, they must be set to modes using different 
VRAM areas. All of the modes listed above are designed to use no more 
than 64K of the video memory map - even modes D-12 conform to this 
limit by organizing their extra memory into "planes".



Mode 13 is in a class by itself, it can provide 256 colors with a 
resolution of 320x200 pels. Each pel requires one byte of VRAM, so 
it takes 64,000 bytes to store a display screen. Using 128K at

A0000, it would be possible to support 640x200 or 320x400 resolution; 
but these modes would not be able to coexist with secondary video 
cards.



The PVGA chip is designed to provide 256 color modes with resolutions 
as high as 640x480 pels. Although this requires 310K of VRAM, a kind 
of bankswitching allows this mode to operate without violating the 
64K limit at any time. (Note: the PVGA hardware supports the use of 
a 128K limit, but software always sets it for 64K in order to conform 
to the "standard".)



Before giving examples of how to read and write extended VRAM, the 
necessary memory and I/O conventions will be described. In a 256 color 
mode the memory assigns one byte for each pel - in addition, the byte's 
offset from the beginning of the VRAM is a function of the X-Y coordinates 
of the pel's position on the display. By convention, the leftmost 
pel of the top scan line is assigned coordinates (0,0) - the X coordinates 
increase moving to the right and Y coordinates increase moving downward. 
The rightmost pel in the last scan line of a 640x480 display has the 
position (639,479). Letting WIDTH represent the number of pels in 
one horizontal scan line (usually 320 or 640), a given pel's relative 
location in memory is:



     OFFSET = (WIDTH*Y)+X



The problem of extended VRAM control is the conversion of this OFFSET 
to an address that can be accessed by an 8088 MOV instruction. In 
mode 13, this is simple - the true address is equal to A0000+OFFSET. 
There is no problem because OFFSET is always less than 64K (since 
WIDTH=320, X@ and Y<200>). In extended modes, the video memory must 
be thought of in two distinct ways - the PVGA's point of view and 
the SYSTEM's. The PVGA addressing can be thought of as the correspondence 
between display coordinates and memory location given in the formula 
for OFFSET. It represents the "true" pel address. The SYSTEM addressing 
must be set up by the software prior to the memory access itself. 
This setup is carried out by writing to certain I/O registers in the 
PVGA. Note that this is not an unusual situation, a similar preparation 
is required to work with standard "planed" memory.



Before describing the SYSTEM memory I/O registers in detail, a model 
of the relationship between PVGA and SYSTEM memory treatment will 
be presented. All memory addresses, unless stated otherwise, are hexadecimal.



The PVGA can control up to one megabyte of video RAM, so the value 
calculated by OFFSET requires twenty bits or five hex digits. It is 
useful to split the five digit address into two parts - the two high 
order digits (HI_OFFSET) and the three low order digits (LO_OFFSET). 
For example, OFFSET=1E7A4 has 1E as a HI_OFFSET and 7A4 as its LO_OFFSET. 




Because SYSTEM video memory addresses begin at A0000 and cannot exceed 
AFFFF, a full five digit OFFSET will go out of range if A0000 is added 
(like for mode 13). To allow the necessary adjustment, the PVGA provides 
two offset registers - PR0A and PR0B. These registers can be used 
to map regions of the VRAM into the SYSTEM memory area. There are 
two options:



     1. Use PR0A alone to access a 64K VRAM window. This window 

        begins at A0000 in SYSTEM memory.



     2. Use PR0A and PR0B to access two 32K VRAM windows. The 

        PR0A window is mapped to A8000 and PR0B to A0000. Even 

        though the windows are separate in SYSTEM memory, they

        can overlap in PVGA memory if the values written to PR0A

        and PR0B specify it.



PR0A and PR0B both take one byte values. Assuming ADDRESS is a twenty 
bit number in the video range A0000-AFFFF, the correspondence between 
PVGA and SYSTEM addressing is given as follows:



     SYSTEM to PVGA is straightforward -



          OFFSET = (ADDRESS-A0000)+VALUE(PR0A)*1000H

      and OFFSET = (ADDRESS-A0000)+VALUE(PR0B)*1000H



     PVGA to SYSTEM is more ambiguous because the same OFFSET

     can be derived from different combinations of ADDRESS and

     VALUE(PR0A). The recommended settings are -



          ADDRESS     = A8000+LO_OFFSET

          VALUE(PR0A) = HI_OFFSET-8h

      and ADDRESS     = A0000+LO_OFFSET

          VALUE(PR0B) = HI_OFFSET



Adherance to this recommendation maximizes the size of the PVGA memory 
blocks that can be modified without worrying about address

wraparound in SYSTEM memory:

     e.g. take OFFSET=2FFFF



     Case 1: set DS:SI=A000:FFF and VALUE(PR0A)=2F



     Case 2: set DS:SI=A000:FFFF and VALUE(PR0A)=20



Both cases yield the correct OFFSET, but if SI is incremented, the 
case 1 OFFSET is 30000 and the case 2 OFFSET has wrapped back to 20000. 
Because LO_OFFSET has a maximum value of FFF (=4K-1), it is safe to 
operate with 60K (=64-4) blocks if PR0A alone is used, and 28K (=32-4) 
if PR0A and PR0B are both activated.



A close check of the PVGA-SYSTEM conversion formulas should reveal 
that the PR0B variants aren't quite as symmetric as those for PR0A. 
In particular, to generate a SYSTEM address that accesses PVGA OFFSETs 
in the range 00000-07FFF, it is necessary to set PR0A to a value in 
the vicinity of 



     (Total 4K units of VRAM used)-8



For example, 640x400 mode requires 256K of video memory which is equivalent 
to a maximum OFFSET of 40000. This value has a HI_OFFSET equal to 
40 which also represents the "Total 4K units of VRAM used". So, by 
setting PR0A to 38 (=40-8) and accessing SYSTEM address A8000, software 
can modify OFFSET 00000. Although this technique allows PR0A to access 
any VRAM that PR0B can, the implementation might be clumsy since the 
"Total 4K units" varies according to the mode's resolution. An alternative 
is to check the value of HI_OFFSET-8 prior to setting PR0A or PR0B 
and making sure that PR0A receives a valid setting. In other words, 
if the source and destination OFFSETs are both less than 08000, use 
PR0A only; otherwise set up PR0B to handle the lower OFFSET and PR0A 
to access the higher OFFSET.



For anyone familiar with the 8088 architecture, PR0A and PR0B can 
be thought of as being analogous to the segment registers DS and ES. 
Both are used to modify a sixteen bit address (e.g. [SI]) to a full 
twenty bit address required by the hardware. The principal difference 
is the granularity - incrementing a segment register by one advances 
the true address by one paragraph (=16 bytes), and incrementing PR0A 
or PR0B jumps the pointer into PVGA memory by 4K (=4096 bytes).



The following diagram illustrates the PVGA-SYSTEM addressing relationship 
in PR0A-only and PR0A-PR0B operation. The numbers represent the beginning 
addresses of 4K memory blocks, so 02000 should be read as "the memory 
from 2000 to 2FFF inclusive":



     Example 1                          Example 2

     PR0A=12                            PR0A=0E=16-8

     PR0B inactive                      PR0B=00



     PVGA      SYSTEM WINDOW            PVGA      SYSTEM WINDOWS

     00000                              00000     A0000 (PR0B)

     01000                              01000     A1000 (PR0B)

     02000                              02000     A2000 (PR0B)

     03000                              03000     A3000 (PR0B)

     04000                              04000     A4000 (PR0B)

     05000                              05000     A5000 (PR0B)

     06000                              06000     A6000 (PR0B)

     07000                              07000     A7000 (PR0B)

     08000                              08000

     09000                              09000

     0A000                              0A000

     0B000                              0B000

     0C000                              0C000

     0D000                              0D000

     0E000                              0E000

     0F000                              0F000

     10000                              10000

     11000                              11000

     12000     A0000 (PR0A)             12000

     13000     A1000 (PR0A)             13000

     14000     A2000 (PR0A)             14000

     15000     A3000 (PR0A)             15000

     16000     A4000 (PR0A)             16000     A8000 (PR0A)

     17000     A5000 (PR0A)             17000     A9000 (PR0A)

     18000     A6000 (PR0A)             18000     AA000 (PR0A)

     19000     A7000 (PR0A)             19000     AB000 (PR0A)

     1A000     A8000 (PR0A)             1A000     AC000 (PR0A)

     1B000     A9000 (PR0A)             1B000     AD000 (PR0A)

     1C000     AA000 (PR0A)             1C000     AE000 (PR0A)

     1D000     AB000 (PR0A)             1D000     AF000 (PR0A)

     1E000     AC000 (PR0A)             1E000     

     1F000     AD000 (PR0A)             1F000

     20000     AE000 (PR0A)             20000

     21000     AF000 (PR0A)             21000

     22000                              22000

     23000                              23000

       .                                  .

       .                                  .

       .                                  .



Now that the operation of PR0A and PR0B has been made abundantly clear, 
their enabling and setting procedures will be outlined. Control is 
handled by a variety of I/O registers provided by the PVGA: PR0A, 
PR0B, PR1, PR2, PR3, PR4 and PR5. These registers are described in 
the PVGA User's Guide; of them, PR0A, PR0B, PR1 and PR5 are associated 
with extended memory control. (So is PR4, but it is set automatically 
whenever an extended memory mode has been activated and should be 
left alone thereafter.)



These registers are implemented as additions to the Graphics Controller 
portion of the PVGA. Software communicates with the Graphics Controller 
through two eight-bit I/O ports - the Index Port at 3CE and the Data 
Port at 3CF. Both ports can be written to with a standard OUT instruction, 
an IN instruction returns the last value sent to the port. The Index 
Port is used to select a specific Graphics Controller register, the 
following indices are assigned within the PVGA:



     Index          Name



      0-8           Standard VGA Graphics Controller Registers

       9            PR0A

      0A            PR0B

      0B            PR1

      0C            PR2

      0D            PR3

      0E            PR4

      0F            PR5



Since PR0A through PR5 are not standard VGA registers, the PVGA chip 
offers optional protection from accidental modification. The PR5 register 
handles this protection in the following manner:



     Example - writing the value 2E to PR0B

     

     MOV  DX,3CE         ; GRAPHICS CONTROLLER INDEX PORT ADDRESS

     MOV  AL,0F          ; PR5 INDEX VALUE

     MOV  AH,5           ; THE VALUE 5 "UNLOCKS" PR0A TO PR4

     OUT  DX,AX          ; UNLOCK

     MOV  AL,0A          ; PR0B INDEX VALUE

     MOV  AH,2E          ; DESIRED SETTING

     OUT  DX,AX          ; SET PR0B

     MOV  AL,0F          ; PR5 INDEX VALUE

     MOV  AH,0           ; VALUES OTHER THAN 5 "LOCK" PR0A TO PR4

     OUT  DX,AX          ; RELOCK



It is not necessary to relock after setting a register, but this is 
a safe convention. PR5 values are recognized modulo 8 (e.g. A5 is 
equivalent to 05).



PR0A is modified in the same way as PR0B. It is probably desirable 
to zero these registers upon completion of an extended memory access 
- this forces consistency between diferent applications. The following 
example shows how to enable PR0B by setting the 8 bit of PR1:



     Example - enabling and disabling PR0B

     

     MOV  DX,3CE         ; GRAPHICS CONTROLLER INDEX PORT ADDRESS

     MOV  AL,0F          ; PR5 INDEX VALUE

     MOV  AH,5           ; THE VALUE 5 "UNLOCKS" PR0A TO PR4

     OUT  DX,AX          ; UNLOCK

     MOV  AL,0B          ; PR1 INDEX VALUE

     OUT  DX,AL          ; SET INDEX ONLY

     INC  DX             ; GRAPHICS CONTROLLER DATA PORT ADDRESS

     IN   AL,DX          ; READ CURRENT CONTENTS

     OR   AL,8           ; SET BIT TO ENABLE PR0B

     OUT  DX,AL          ; ENABLE PR0B

     ---                 ; INSERT CODE TO MODIFY MEMORY,

                         ; LEAVING REGISTERS AND PORTS UNCHANGED

     AND  AL,0F7         ; SET BIT TO DISABLE PR0B

     OUT  DX,AL          ; DISABLE PR0B

     DEC  DX             ; GRAPHICS CONTROLLER INDEX PORT ADDRESS

     MOV  AL,0F          ; PR5 INDEX VALUE

     MOV  AH,0           ; VALUES OTHER THAN 5 "LOCK" PR0A TO PR4

     OUT  DX,AX          ; RELOCK


It is also possible to read and write PR0A and PR0B using the extended
BIOS calls described in the document PROGRAM.TXT.  When using these BIOS
calls it is not necessary to unlock and lock the extended registers since
this is performed automatically by the BIOS routines.


     Example - writing the value 2E to PROB using BIOS

     
     MOV  AX,007F	; Extended function BIOS call

     MOV  BH,0A		; Write to PR0B

     MOV  BL,2E		; Value to write to PR0B

     INT  10		; Video BIOS software interrupt



     Example - enabling and disabling PROB using BIOS


     MOV  AX,007F	; Extended function BIOS call

     MOV  BH,1B		; Read contents of PR1

     INT  10		; Video BIOS software interrupt

     OR   BL,8		; Set bit of PR1 to enable PR0B

     MOV  BH,0B		; Write value to PR1 using BIOS call

     INT  10		; Video BIOS software interrupt

     ___		; Insert code to modify memory

     			; Leaving registers and ports unchanged

     AND  BL,0F7	; Set bit of PR1 to disable PR0B

     MOV  BH,0B		; Write value to PR1 using BIOS call

     INT 10		; Video BIOS software interrupt 


In summary, control through the PR0A and PR0B registers allows software 
to access any part of extended video memory without violating the 
PC and VGA memory map standards. There are two access options - PR0A 
is most useful for simple memory fills and for moving VRAM blocks 
that lie within 64K of each other. But if the lowest OFFSET of one 
block is more than 64K bytes from the highest OFFSET of the other, 
it is necessary to use both PR0A and PR0B. To avoid address wrapping 
or conflict with other software, the following conventions are recommended:



1. Upon completion of a task, make sure that:

     a. PR0A and PR0B are zeroed

     b. PR0B is disabled

     c. PR0A through PR4 are locked by setting PR5



2. When converting an OFFSET to its equivalent SYSTEM address,

   use the PR0A and PR0B registers to their greatest advantage

   by splitting OFFSET into HI_OFFSET and LO_OFFSET as described

   earlier.



3. Use 32K as a maximum memory block size for PR0A operations, 

   and use 16K for PR0A-PR0B transfers. In conjunction with

   convention 2, this avoids any address wrap problems. Also, 

   as multiples of 4K, these block sizes are convenient to deal

   with in loops.



Sample program 1



     Task: zero 256K of video memory starting at OFFSET 00000



     Assuming: registers unlocked, PR0A enabled, PR0B disabled



          MOV  AX,0A000

          MOV  ES,AX          ; SET VIDEO RAM SEGMENT

          MOV  DX,3CE         ; GRAPHICS CONTROLLER INDEX PORT

          MOV  BL,0           ; INITIAL VALUE FOR HI_OFFSET

LOOP1:                        ; OUTER LOOP FILLS 32K BLOCKS

          MOV  AL,9           ; INDEX FOR PR0A

          MOV  AH,BL          ; HI_OFFSET FOR CURRENT 32K

          OUT  DX,AX          ; SET IT

          CLD

          MOV  DI,0           ; SET LO_OFFSET

          MOV  AL,0           ; DATA VALUE

          MOV  CX,8000H       ; 32K BYTES

          REP  STOSB          ; FILL 32K WITH ZERO

          ADD  BL,8           ; ADVANCE HI_OFFSET BY 32K

          CMP  BL,40          ; TEST IF HI_OFFSET OF 256K

          JNZ  LOOP1





Sample program 2



     Task: Move the contents of a source VRAM block to a

           destination VRAM block.



     Assuming: registers unlocked

               PR0A enabled

               PR0B enabled

               pointer and size variables are in code segment

               08000 = destination OFFSET

               source, destination blocks disjoint

               SIZE+either OFFSET = highest possible OFFSET



          DB   SOURCE_HI      ; HI_OFFSET FOR SOURCE BLOCK

          DW   SOURCE_LO      ; LO_OFFSET FOR SOURCE BLOCK

          DB   DESTINATION_HI ; HI_OFFSET FOR DESTINATION

          DW   DESTINATION_LO ; LO_OFFSET FOR DESTINATION

          DD   SIZE           ; BLOCK SIZE IN BYTES



          MOV  AX,0A000

          MOV  DS,AX          ; SET SOURCE VRAM SEGMENT

          MOV  BL,CS:BYTE PTR SOURCE_HI

          MOV  AX,0A800

          MOV  ES,AX          ; SET DESTINATION VRAM SEGMENT

          MOV  BH,CS:BYTE PTR DESTINATION_HI

          SUB  BH,8           ; PR0A ADJUSTMENT OF HI_OFFSET

          MOV  DX,3CE         ; GRAPHICS CONTROLLER INDEX PORT

LOOP1:                        ; OUTER LOOP MOVES 16K BLOCKS

          MOV  CX,4000        ; DEFAULT BLOCK SIZE OF 16K

          CMP  CS:WORD PTR SIZE+2,0

          JNZ  MORE           ; SIZE>64K, CONTINUE

          CMP  CX,CS:WORD PTR SIZE

          JBE  MORE           ; SIZE=16K, CONTINUE

          MOV  CX,CS:WORD PTR SIZE      ; SET COUNT=MIN(16K,SIZE)

          JCXZ EXIT           ; SIZE=0, DONE

MORE:     MOV  AL,0A          ; INDEX FOR PR0B

          MOV  AH,BL          ; HI_OFFSET FOR SOURCE

          OUT  DX,AX          ; SET IT

          MOV  AL,9           ; INDEX FOR PR0A

          MOV  AH,BH          ; HI_OFFSET FOR DESTINATION

          OUT  DX,AX          ; SET IT

          MOV  SI,CS:WORD PTR SOURCE_LO ; SET LO_OFFSETS

          MOV  DI,CS:WORD PTR DESTINATION_LO

          PUSH CX             ; SAVE SUB-BLOCK SIZE

          CLD

          REP  MOVSB          ; TRANSFER BLOCK PORTION (16K MAX)

          ADD  BL,4           ; ADVANCE SOURCE BY 16K

          ADD  BH,4           ; ADVANCE DESTINATION BY 16K

          POP  CX             ; RECOVER SUB-BLOCK SIZE

          SUB  CS:WORD PTR SIZE,CX      ; SUBTRACT PART MOVED

          SBB  CS:WORD PTR SIZE+2,0     ; BORROW FROM HI ORDER

          JMP  SHORT LOOP1

EXIT:     ---


* Paradise and VGA Plus 16 are trademarks of Paradise Systems, Inc.
  IBM and AT are trademarks of International Business Machines Corp.

(C) 1988 Paradise Systems, Inc.  All rights reserved.  